package com.sdses.weather.config;

import cn.hutool.core.util.StrUtil;
import com.sdses.weather.config.exception.BusinessException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.beans.Introspector;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;

@Component
@ConfigurationProperties(prefix = "weather")
@Data
@Slf4j
public class WeatherProviderSchemaConfig {

    public static final Map<String, Function<ProviderConfig, DataTypeConfig>> TYPE_GETTER_MAP = buildTypeGetterMap();
    private Map<String, ProviderConfig> providers = new LinkedHashMap<>();

    @Data
    public static class ProviderConfig {
        private String basePath = "$"; // JSONPath 根路径

        private DataTypeConfig hourly;
        private DataTypeConfig alert;
        private DataTypeConfig condition;
        private DataTypeConfig daily;
        private DataTypeConfig minutely;
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class FieldMapping {
        private String json;
        private String java;
        private String type = "string";
        private String desc;
        private String converter;
        private String sortType; //排序时按什么类型比较：int, long, double, date
        private String sortFormat; //仅当 sortType=date 时使用 如果是 date，指定时间格式

    }

    @Data
    public static class DataTypeConfig {
        private List<String> arrayPaths = new ArrayList<>();
        private String targetField;
        private boolean arrayFlag = true;
        private boolean flatten = false;
        private List<FieldMapping> fields;
        private List<FieldMapping> contextFields;
    }

    public ProviderConfig getProvider(String providerName) {
        if (providers == null || StrUtil.isEmpty(providerName)) {
            throw new BusinessException("找不到服务商：" + providerName);
        }

        ProviderConfig providerConfig = providers.get(providerName);
        if (providerConfig == null) {
            throw new BusinessException("暂不支持服务商：" + providerName);
        }
        return providerConfig;
    }

    public static DataTypeConfig getDataTypeConfig(ProviderConfig cfg, String type) {
        Function<ProviderConfig, DataTypeConfig> getter = TYPE_GETTER_MAP.get(type);
        if (getter == null) {
            throw new BusinessException("配置有问题厂商配置ProviderConfig:" + type);
        }
        return getter.apply(cfg);
    }

    // 自动构建映射：扫描所有返回 DataTypeConfig 的 getter
    public static Map<String, Function<ProviderConfig, DataTypeConfig>> buildTypeGetterMap() {
        Map<String, Function<ProviderConfig, DataTypeConfig>> map = new HashMap<>();

        // 获取所有 getter 方法
        Method[] methods = ProviderConfig.class.getMethods();
        for (Method method : methods) {
            // 方法名必须是 getXXX 且无参
            if (!method.getName().startsWith("get") || method.getParameterCount() != 0) {
                continue;
            }
            // 返回类型必须是 DataTypeConfig
            if (method.getReturnType() != DataTypeConfig.class) {
                continue;
            }

            // 提取字段名：getCondition -> "condition"
            String methodName = method.getName();
            String key = Introspector.decapitalize(methodName.substring(3)); // 去掉 get 并小写首字母

            // 创建 Function：ProviderConfig -> DataTypeConfig
            Function<ProviderConfig, DataTypeConfig> getter = config -> {
                try {
                    return (DataTypeConfig) method.invoke(config);
                } catch (Exception e) {
                    throw new IllegalStateException("调用 getter 失败: " + method.getName(), e);
                }
            };

            map.put(key, getter);
        }

        if (map.isEmpty()) {
            throw new IllegalStateException("未找到任何返回 DataTypeConfig 的 getter 方法");
        }

        log.info("自动注册 WeatherProviderSchemaConfig 类型映射: {}", map.keySet());
        return Collections.unmodifiableMap(map);
    }

}